把coroutines跟retrofit結合進recyclerview
這邊最需要注意的是如果你retrofit拿到的值是int而不是string必須要stringvalueof(),否則android會預設為xml的數值而報錯
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="Post"
type="com.example.myapplication.Post" />
<variable
name="viewmodel"
type="com.example.myapplication.ViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{()-> viewmodel.onClick(Post)}">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:padding="5dp"
android:textSize="16sp"
android:text="@{String.valueOf(Post.id)}" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
這邊第25行就是數值轉換
activity
class Activity:AppCompatActivity() {
lateinit var binding: ActivityMainBinding
private val mViewModel by lazy {
initViewModel(application, ViewModel::class.java)
}
private val mAdapter by lazy {
Adapter<Post>(R.layout.item_view,mViewModel)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.recyclerView.adapter = mAdapter
mViewModel.data.observe(this, Observer { post ->
mAdapter.updateData(post)
})
mViewModel.coroutine()
}
}
adapter
class Adapter<T>(
@LayoutRes val resource: Int,
val viewmodel:ViewModel
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val data: List<T>
get() = _data.toList()
private val _data: MutableList<T> = mutableListOf()
fun updateData(list: ArrayList<T>){
_data.clear()
_data.addAll(list)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = DataBindingUtil.inflate<ViewDataBinding>(layoutInflater, resource, parent,false)
return MyViewHolder(binding)
}
override fun getItemCount(): Int {
return data.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is Adapter<*>.MyViewHolder){
val item = data[position]
if (item != null)
holder.setupData(item)
}
}
inner class MyViewHolder( var binding: ViewDataBinding):RecyclerView.ViewHolder(binding.root){
fun setupData(post: Any){
binding.setVariable(BR.Post, post)
binding.setVariable(BR.viewmodel,viewmodel)
}
}
}
viewmodel
class ViewModel(application: Application): AndroidViewModel(application) {
val data = MutableLiveData<ArrayList<Post>>()
fun onClick(post: Post){
Toast.makeText(getApplication(), post.id, Toast.LENGTH_SHORT).show()
}
private val myScope = object: CoroutineScope{
override val coroutineContext: CoroutineContext
get() = job
}
private val job = Job()
fun coroutine(){
myScope.launch(Dispatchers.Main){
// progressBar.visibility = View.VISIBLE
retrofit()
// progressBar.visibility = View.GONE
}
}
private suspend fun retrofit() = withContext(Dispatchers.IO){
val response = GoRetrofit.api.getAPI()
if (response.isSuccessful) {
data.postValue(response.body() as ArrayList<Post>)
}
}
override fun onCleared() {
myScope.cancel()
super.onCleared()
}
}
這邊要注意的是postvalue(),如果在main線程是可以使用setvalue(),但如過是在用後台線程就必須要用postvalue()